﻿using Microsoft.Xna.Framework;

namespace BlockeyEssentials
{
    public abstract class Moveable
    {

        #region Constructors

        public Moveable()
            : base()
        {
            this.Position = Vector2.Zero;
            this.PositionPrevious = Vector2.Zero;
            this.Velocity = Vector2.Zero;
            this.VelocityPrevious = Vector2.Zero;
            this.Acceleration = 0.25f;
            this.Decceleration = 0.98f;
            this.Mass = 0.0f;
            this.CollisionCircle = new Circle(1.0f);
            this.JumpHeight = 0.0f;
            this.JumpSpeed = 0.0f;
        }

        #endregion

        #region Methods

        /// <summary>
        /// Moves the Moveable along the screen
        /// </summary>
        public virtual void Move()
        {
            //// -- Slows Down Moveable -- //
            if (Velocity.Length() > 0)
                Velocity *= Decceleration;

            //// -- Stop Moveable -- ////
            if (Velocity.Length() < 0.05f)
                Velocity = Vector2.Zero;

            //// -- Limits Speed -- ////
            if (Velocity.Length() > CollisionCircle.Diameter)
            {
                Vector2 oVelDirection = Velocity;
                if (oVelDirection != Vector2.Zero)
                    oVelDirection.Normalize();

                Velocity = oVelDirection * CollisionCircle.Radius;
            }

            //// -- Applies Gravity -- ////
            if (JumpHeight > 0)
                JumpSpeed -= 0.25f;

            //// -- Keeps Moveable on the ground -- ////
            if (JumpHeight <= 0)
            {
                JumpHeight = 0;
                JumpSpeed = 0;
            }

            //// -- Moves the Moveable -- ////
            PositionPrevious = Position;
            Position += Velocity;
            CollisionCircle.Position = Position;
            JumpHeight += JumpSpeed;
        }

        /// <summary>
        /// Determines if there is a collision between two moveables.
        /// </summary>
        /// <param name="a">Moveable A</param>
        /// <param name="b">Moveable B</param>
        public static void Collision(Moveable a, Moveable b)
        {
            Circle oCircleA = a.CollisionCircle;
            Circle oCircleB = b.CollisionCircle;

            //// -- Collision Detected -- ////
            if (Circle.Collision(oCircleA, oCircleB))
            {
                LineSegment oAB = new LineSegment(a.Position, b.Position);
                LineSegment oBA = oAB.BtoA;
                float oMassForce;
                float oVelForce = (a.Velocity.Length() + b.Velocity.Length()) / 2;
                float oScale = 0.75f;

                //// -- Moveable A Collision -- ////
                oMassForce = (a.Mass + b.Mass) / (a.Mass * 2);
                a.Velocity = oBA.Direction * (oMassForce * oVelForce * oScale);
                if (a.Velocity.Length() > a.CollisionCircle.Diameter)
                {
                    Vector2 oDirection = a.Velocity;
                    oDirection.Normalize();
                    a.Velocity = oDirection * a.CollisionCircle.Diameter;
                }

                //// -- Moveable B Collision -- ////
                oMassForce = (a.Mass + b.Mass) / (b.Mass * 2);
                b.Velocity = oAB.Direction * (oMassForce * oVelForce * oScale);
                if (b.Velocity.Length() > b.CollisionCircle.Diameter)
                {
                    Vector2 oDirection = b.Velocity;
                    oDirection.Normalize();
                    b.Velocity = oDirection * b.CollisionCircle.Diameter;
                }
            }
        }

        #endregion

        #region Properties

        public Vector2 Position
        {
            get { return _Position; }
            set { _Position = value; }
        }

        public Vector2 PositionPrevious
        {
            get { return _PositionPrevious; }
            set { _PositionPrevious = value; }
        }

        public Vector2 Velocity
        {
            get { return _Velocity; }
            set { _Velocity = value; }
        }

        public Vector2 VelocityPrevious
        {
            get { return _VelocityPrevious; }
            set { _VelocityPrevious = value; }
        }

        public float Acceleration
        {
            get { return _Acceleration; }
            set { _Acceleration = value; }
        }

        public float Decceleration
        {
            get { return _Decceleration; }
            set { _Decceleration = value; }
        }

        public float Mass
        {
            get { return _Mass; }
            set { _Mass = value; }
        }

        public Circle CollisionCircle
        {
            get { return _CollisionCircle; }
            set { _CollisionCircle = value; }
        }

        public float JumpHeight
        {
            get { return _JumpHeight; }
            set { _JumpHeight = value; }
        }

        public float JumpSpeed
        {
            get { return _JumpSpeed; }
            set { _JumpSpeed = value; }
        }

        #endregion

        #region Fields

        private Vector2 _Position;
        private Vector2 _PositionPrevious;
        private Vector2 _Velocity;
        private Vector2 _VelocityPrevious;
        private float _Acceleration;
        private float _Decceleration;
        private float _Mass;
        private float _JumpHeight;
        private float _JumpSpeed;
        private Circle _CollisionCircle;

        #endregion

    }
}
